package lumis.util.converter;

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;

import lumis.util.log.ILogger;
import lumis.util.log.LoggerFactory;

/**
 * This converter is able to do conversion between {@link Number} subtypes in
 * the java.lang package and String.
 * 
 * @version $Revision: 11556 $ $Date: 2010-05-13 19:16:35 -0300 (Thu, 13 May 2010) $
 * @since 4.0.4
 */
public class NumberConverter extends AbstractConverter
{
	private static ILogger logger = LoggerFactory.getLogger(NumberConverter.class);

	/**
	 * Creates the NumberFormat for the given locale and pattern.
	 * 
	 * @param locale
	 *            the locale.
	 * @param pattern
	 *            the pattern.
	 * @return the DecimalFormat created.
	 * @see NumberFormat
	 * @since 4.0.4
	 */
	protected NumberFormat createNumberFormat(Locale locale, String pattern)
	{
		DecimalFormat formatter = new DecimalFormat();
		if (locale != null)
		{
			DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols(locale);
			formatter.setDecimalFormatSymbols(decimalFormatSymbols);
		}
		else
			logger.debug("No locale provided, using jvm default.");

		if (pattern != null)
			formatter.applyPattern(pattern);
		else
			logger.debug("No pattern provided, using jvm default.");

		return formatter;
	}

	@SuppressWarnings("unchecked")
	@Override
	protected <T> T doConversion(Class<T> type, Object value, Locale locale, String pattern) throws UnsupportedConversionTypeException
	{
		Number numberValue;
		if (value instanceof Number)
			numberValue = (Number) value;
		else if (value instanceof CharSequence)
		{
			// convert value to a Number
			try
			{
				String stringValue = value.toString();
				if ("true".equals(stringValue))
					numberValue = new Integer(1);
				else if ("false".equals(stringValue))
					numberValue = new Integer(0);
				else
				{
					NumberFormat formatter = createNumberFormat(locale, pattern);
					numberValue = formatter.parse(value.toString());
				}
			}
			catch (ParseException e)
			{
				throw new ConversionException(e);
			}
		}
		else
			throw new UnsupportedConversionTypeException(value.getClass().getName());

		if (Number.class.isAssignableFrom(type))
		{
			// convert the number to type and return it
			if (Integer.class.equals(type))
				return (T) new Integer(numberValue.intValue());
			else if (Double.class.equals(type))
				return (T) new Double(numberValue.doubleValue());
			else if (Float.class.equals(type))
				return (T) new Float(numberValue.floatValue());
			else if (Long.class.equals(type))
				return (T) new Long(numberValue.longValue());
			else if (Short.class.equals(type))
				return (T) new Short(numberValue.shortValue());
			else if (Byte.class.equals(type))
				return (T) new Byte(numberValue.byteValue());
			else if (Number.class.equals(type))
				return (T) numberValue;
			else
				throw new UnsupportedConversionTypeException(type.getName());
		}
		else if (type.isAssignableFrom(String.class))
		{
			// convert the number to string using a number format and return it
			NumberFormat formatter = createNumberFormat(locale, pattern);
			return (T) formatter.format(value);
		}
		else
			throw new UnsupportedConversionTypeException(type.getName());
	}

}
